package com.core.EncryptedCommunication;


import com.common.ErrorCodeEnum;
import com.common.anntation.SecurityParameter;
import com.common.config.Config;
import com.common.exception.BusinessException;
import com.common.utils.AesEncryptUtils;
import com.common.utils.ServletUtils;
import com.core.springSecurity.service.TokenService;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * 对请求过来的数据进行解密
 * */
@RestControllerAdvice
public class DecodeRequestBodyAdvice implements RequestBodyAdvice {


  private static final Logger logger = LoggerFactory.getLogger(DecodeRequestBodyAdvice.class);


  @Override
  public boolean supports(
          MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
    return true;
  }

  @Override
  public Object handleEmptyBody(
          Object body,
          HttpInputMessage httpInputMessage,
          MethodParameter methodParameter,
          Type type,
          Class<? extends HttpMessageConverter<?>> aClass) {
    return body;
  }

  @Override
  public HttpInputMessage beforeBodyRead(
          HttpInputMessage inputMessage,
          MethodParameter methodParameter,
          Type type,
          Class<? extends HttpMessageConverter<?>> aClass) {
    if (!Config.isFlag()) {
      return inputMessage;
    }
    try {
      boolean encode = false;
      // 方法不存在则在类上寻找注解则在类上寻找注解
      if (Objects.requireNonNull(methodParameter.getMethod()).isAnnotationPresent(SecurityParameter.class)
              || methodParameter.getDeclaringClass().isAnnotationPresent(SecurityParameter.class)) {
        // 获取注解配置的包含和去除字段
        SecurityParameter serializedField = methodParameter.getMethodAnnotation(SecurityParameter.class);
        if (serializedField == null) {
          serializedField = methodParameter.getDeclaringClass().getAnnotation(SecurityParameter.class);
        }
        // 入参是否需要解密
        if ((serializedField != null && serializedField.inDecode())) {
          encode = true;
        }
      }
      if (encode) {
        return new MyHttpInputMessage(inputMessage);
      } else {
        return inputMessage;
      }
    } catch (Exception e) {
      throw new BusinessException(ErrorCodeEnum.PARAM_FAILED);
    }
  }

  @Override
  public Object afterBodyRead(
          Object body,
          HttpInputMessage httpInputMessage,
          MethodParameter methodParameter,
          Type type,
          Class<? extends HttpMessageConverter<?>> aClass) {
    return body;
  }

  static class MyHttpInputMessage implements HttpInputMessage {
    private final HttpHeaders headers;

    private final InputStream body;

    public MyHttpInputMessage(HttpInputMessage inputMessage) {
      this.headers = inputMessage.getHeaders();
      final HttpServletRequest request = ServletUtils.getRequest();
      final String token = request.getHeader(TokenService.getHeader());
      try {
        if (StringUtils.isNoneBlank(token)) {
          this.body = IOUtils.toInputStream(AesEncryptUtils.decrypt(
                  checkParameters(IOUtils.toString(inputMessage.getBody(), String.valueOf(StandardCharsets.UTF_8))), token.substring(0, 16)), String.valueOf(StandardCharsets.UTF_8));
        } else {
          this.body = IOUtils.toInputStream(AesEncryptUtils.decrypt(
                  checkParameters(IOUtils.toString(inputMessage.getBody(), String.valueOf(StandardCharsets.UTF_8)))), String.valueOf(StandardCharsets.UTF_8));
        }
      } catch (Exception e) {
        throw new BusinessException(ErrorCodeEnum.PARAM_FAILED);
      }
    }

    @Override
    public InputStream getBody() {
      return body;
    }

    @Override
    public HttpHeaders getHeaders() {
      return headers;
    }

    /**
     * 入参检查
     *
     * @param requestData 入参
     * @return 返回请求参数
     */
    public String checkParameters(String requestData) {
      if (StringUtils.isNoneBlank(requestData)) {
        String s = "{\"requestData\":";
        if (!requestData.startsWith(s)) {
          throw new BusinessException(ErrorCodeEnum.PARAM_FAILED);
        } else {
          int closeLen = requestData.length() - 1;
          int openLen = "{\"requestData\":".length();
          return StringUtils.substring(requestData, openLen, closeLen);
        }
      }
      return "";
    }
  }
}
